﻿using System;
using System.Collections.Generic;
using System.Linq;
using Loki.Engine;
using Loki.Model;
using Luna.Data;
using Luna.Data.Accounting;

namespace Luna.Model.Accounting
{
    public class AccountingService : ApplicationService<LunaModel, LunaDataModel>, IAccountingService
    {
        public AccountingService()
        {
            DefineTypeCreation<IAccountingCategory, AccountingCategory>();
            DefineTypeCreation<IAccountOperation, AccountOperation>();
            DefineTypeCreation<IEditableAccount, Account>();

            _Categories = CreateContainer<IAccountingCategory, Guid>(CacheConfiguration.CATEGORIES);
            _Accounts = CreateContainer<IAccount, Guid>(CacheConfiguration.ACCOUNTS);
            _Operations = CreateContainer<AccountOperation, Guid>(CacheConfiguration.OPERATIONS);
        }

        protected IAccountingDataProvider DataProvider
        {
            get { return DataContext.Accounting; }
        }

        private const string _ID = "{789F545F-690A-421D-92CA-22F211472264}";

        private const string NAME = "Luna accounting service";

        public override Guid ID
        {
            get { return new Guid(_ID); }
        }

        public override string Name
        {
            get { return NAME; }
        }

        #region Accounting categories

        private EntityContainer<IAccountingCategory, Guid> _Categories;

        public IIndexedEnumerable<IAccountingCategory, Guid> Categories
        {
            get { return _Categories; }
        }

        public void Save(IAccountingCategory P_Category)
        {
            DataProvider.SaveCategory(P_Category);
            _Categories.Register(new AccountingCategory(DataProvider.GetCategory(P_Category.ID)));
        }

        public bool CanDelete(IAccountingCategory P_Category)
        {
            return true;
        }

        public void Delete(IAccountingCategory P_Category)
        {
            DataProvider.DeleteCategory(P_Category.PK);
            _Categories.Remove(P_Category);
        }
        #endregion

        #region Operations
        private EntityContainer<AccountOperation, Guid> _Operations;

        public IIndexedEnumerable<IAccountOperation, Guid> Operations
        {
            get { return _Operations; }
        }
        #endregion

        #region Accounts
        private EntityContainer<IAccount, Guid> _Accounts;

        public IIndexedEnumerable<IAccount, Guid> Accounts
        {
            get { return _Accounts; }
        }

        public void Save(IAccount P_Account)
        {
            Account L_Account = P_Account as Account;
            if (L_Account != null)
            {
                var L_ValidOperations = L_Account.Operations.Where(x => x.ExecutionDate >= L_Account.StartDate);

                // Business logic
                L_Account.CurrentAmount = L_Account.InitialAmount + L_ValidOperations.Sum(x => x.Amount);
                L_Account.ValidatedAmount = L_Account.InitialAmount + L_ValidOperations.Where(x => x.ValueDate.HasValue).Sum(x => x.Amount);

                if (L_Account.Operations.Any(x => x.ValueDate.HasValue))
                {
                    L_Account.ValidatedDate = L_ValidOperations.Where(x => x.ValueDate.HasValue).Max(x => x.ValueDate.Value);
                }
                else
                {
                    L_Account.ValidatedDate = L_Account.StartDate;
                }

                List<IOtherOperationData> L_OtherOperations = new List<IOtherOperationData>();
                foreach (var L_Operation in P_Account.Operations)
                {
                    IOtherOperationData L_Data = DataProvider.Create<IOtherOperationData>();
                    L_Data.AccountID = L_Account.ID;
                    L_Data.Amount = L_Operation.Amount;
                    L_Data.CategoryID = L_Operation.Category.ID;
                    L_Data.ProjectID = L_Operation.Project == null ? null : (Guid?)L_Operation.Project.ID;
                    L_Data.Description = L_Operation.Description;
                    L_Data.ExecutionDate = L_Operation.ExecutionDate;
                    L_Data.OperationType = AccountOperationType.Others;
                    L_Data.PK = L_Operation.ID;
                    L_Data.ValueDate = L_Operation.ValueDate;
                    L_OtherOperations.Add(L_Data);
                }

                // persist data
                DataProvider.SaveAccount(L_Account, L_OtherOperations);

                // refresh cache
                Account L_NewAccount = new Account(DataProvider.GetAccount(P_Account.ID));
                _Accounts.Register(L_NewAccount);
                _Operations.ReloadKey(x => x.AccountID, P_Account.ID);
            }
        }

        public bool CanDelete(IAccount P_Account)
        {
            return true;
        }

        public void Delete(IAccount P_Account)
        {
            DataProvider.DeleteAccount(P_Account.ID);
            _Accounts.Remove(P_Account);
        }
        #endregion
    }
}
